Skip to content

Release throttle permits only once#2280

Merged
hyperxpro merged 1 commit into
AsyncHttpClient:mainfrom
maygemdev:perf/release-permit-once
Jul 24, 2026
Merged

Release throttle permits only once#2280
hyperxpro merged 1 commit into
AsyncHttpClient:mainfrom
maygemdev:perf/release-permit-once

Conversation

@pavel-ptashyts

Copy link
Copy Markdown
Contributor

Summary

  • release each throttle semaphore permit at most once
  • continue forwarding both completion and failure callbacks to the handler
  • cover reentrant completion-to-failure callback delivery with a regression test

Root cause

ReleasePermitOnComplete released the same acquired permit from both
onCompleted and onThrowable. A completion callback can synchronously cancel
its future and reenter the wrapper through onThrowable, causing both callback
frames to release the permit. This inflated the semaphore count and raised
java.lang.Error: Maximum permit count exceeded at the maximum count.

Validation

  • JDK 11 regression test reproduced the reported error before the fix
  • JDK 11 focused suite: 8 tests passed
  • clean rerun of the regression tests and unrelated failed classes: 37 tests
    passed
  • full JDK 11 verification was attempted twice; one run hit an existing
    ClientStatsTest timeout-boundary failure, and another encountered transient
    test-class artifact corruption; the final rerun was stopped at the operator's
    request

Closes #1797

Codex on behalf of Pavel Ptashyts

A completion callback can reentrantly trigger onThrowable when a reactive
subscriber disposes and cancels its future. ReleasePermitOnComplete then
released the same acquired permit from both terminal callbacks, inflating
the semaphore and overflowing when its count reached Integer.MAX_VALUE.

Guard the release atomically while continuing to forward both callbacks
to the wrapped handler. Add a regression test reproducing the reported
completion-to-cancellation callback sequence.

Fixes AsyncHttpClient#1797

Codex on behalf of Pavel Ptashyts

Co-Authored-By: Codex <[email protected]>
@hyperxpro
hyperxpro merged commit 5209021 into AsyncHttpClient:main Jul 24, 2026
13 checks passed
hyperxpro added a commit that referenced this pull request Jul 24, 2026
Motivation:

PR #2280 covered the reentrant release case, but left several
permit-release paths untested. In particular, non-reentrant callback
paths and concurrent double-release races were not verified.

Modification:

Add four tests to `ReleasePermitOnCompleteTest` covering the
`onCompleted()` exception path, single-callback release baselines, and a
concurrent cross-thread double-release race.

Result:

Better test coverage for edge cases
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

java.lang.Error: Maximum permit count exceeded

2 participants